/*******************************************************************************
 * Copyright 2007 Vidar Svansson
 *     
 * This file is part of JAdapter.
 * 
 * JAdapter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * JAdapter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with JAdapter.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/


package org.jadapter;

import java.util.LinkedList;
import java.util.List;

public class Components {

    private static Components instance;

    private List<JAdapter<?,?>> adapters;
    private List<Object> utilities;

    private static Nil nil = new Nil();
    
    private Components() {
        if(instance == null) {
            instance = this;
            adapters = new LinkedList<JAdapter<?,?>>();
            utilities = new LinkedList<Object>();	        
        }
    }


    @SuppressWarnings("unchecked") 
    public static <T> T get(Class<T> type) {
        for(Object utility : getInstance().utilities) {
            if(utility instanceof JUtility){
                JUtility util = (JUtility)utility;
                if(type.isAssignableFrom(util.getProvides()))
                    return (T)util.getUtility();
            }
            if(type.isInstance(utility))
                return (T)utility;
        }	
        return create(type);
    }
    
    
    
    @SuppressWarnings("unchecked") 
    public static <T> T get(Class<T> type, Class<?> context){
        for(Object utility : getInstance().utilities) {
            if(utility instanceof JUtility){
                JUtility util = (JUtility)utility;
                if(type.isAssignableFrom(util.getProvides()) 
                        && util.getFor() != null 
                        && context.isAssignableFrom(util.getFor()))
                    return (T)util.getUtility();
            }
        }   
        return null;        
    }    

    @SuppressWarnings("unchecked")
    public static <T> T get(Class<T> type, Object context) {
        if(getInstance().adapters == null)
            return null;
        for(JAdapter adapter : getInstance().adapters) {
            if(adapter.provides(type, context)){
                return (T)adapter.getAdapter(context);
            }
        }
        return null;
    }    
    
    @SuppressWarnings("unchecked") 
    public static <T> T create(Class<T> type) {
        return get(type, nil);
//        for(JAdapter adapter : getInstance().adapters) {
//            if(adapter.provides(type, Nil.class)){
//                return (T)adapter.getAdapter(null);
//            }
//        }
//        return null;
    }    
    
    public static <T,C> T create(Class<T> type, Class<C> context) {
        //return get(type, context);
        for(JAdapter adapter : getInstance().adapters) {
            if(adapter.provides(type, context)){
                return (T)adapter.getAdapter(null);
            }
        }
        return null;
    }

    public static void register(JAdapter<?,?> adapter) {
        getInstance().adapters.add(adapter);
    }

    public static void register(Object utility){
        getInstance().utilities.add(utility);
    }

    public static void register(Class<?> iface, Object utility) {
        getInstance().utilities.add(utility);
    }	

    public static <PROVIDES> void register(Class<PROVIDES> type, 
            Class<? extends PROVIDES> implementation) {
        
        register(type, implementation, Nil.class);
        
        //JAdapter<PROVIDES,Nil> util = new JAdapter<PROVIDES,Nil>();
        //util.setProvides(type);
        //util.setAdapter(implementation);
        //util.setFor(Nil.class);
        
        //register(util);
    }    
    
    public static <PROVIDES,FOR> void register(Class<PROVIDES> type, 
            PROVIDES utility, Class<FOR> context) {
        
        JUtility<PROVIDES,FOR> util = new JUtility<PROVIDES,FOR>();
        util.setFor(context);
        util.setProvides(type);
        util.setUtility(utility);
               
        register(util);
    }

    @SuppressWarnings("unchecked")
    public static <PROVIDES,FOR> void register(
            Class<PROVIDES> provides, 
            Class<? extends PROVIDES> adapter, 
            Class<FOR> adaptable) {
        JAdapter<PROVIDES,FOR> jadapter = new JAdapter<PROVIDES,FOR>();
        jadapter.setProvides(provides);
        jadapter.setFor(adaptable);
        jadapter.setAdapter(adapter);
        register(jadapter);
    }

    public static Components getInstance() {
        if(instance == null)
            new Components();
        return instance;
    }

    public static void destroy(){
        instance = null;
    }

}
